/**
 * Copyright &copy; 2016-2017 <a href="http://git.oschina.net/whoamien/backend_management">Backend Management</a> All rights reserved.
 */
package com.xw.app.generator.util;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.io.Writer;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.stream.Collectors;

import org.apache.commons.lang3.exception.ExceptionUtils;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.xw.app.vo.CodeGenerateResult;
import com.xw.util.FileUtil;

import freemarker.cache.FileTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;

/**
 * The Class GenerateUtil.
 *
 * @author Xi Wei
 */
public class GenerateUtil {

	private static List<String> ignoreFileList = Arrays.asList(".svn",".git",".gitignore","macro.include"); 
	
	/**
	 * Checks if is ignore file.
	 *
	 * @param file the file
	 * @return true, if is ignore file
	 */
	public static boolean isIgnoreFile(File file){
		if(ignoreFileList.contains(file.getName())){
			return true;
		}
		return false;
	}
	
	/**
	 * Gets the all processing file.
	 *
	 * @param dir the dir
	 * @return the all processing file
	 */
	public static List<File> getAllProcessingFile(File dir){
		List<File> fileList = FileUtil.listFilesInDir(dir);
		return fileList.stream().filter(x -> x.isFile()).collect(Collectors.toList());
	}
	
	/**
	 * Scan templates and process.
	 *
	 * @param templateFolder the template folder
	 * @param templateModel the template model
	 * @return the code generate result
	 */
	public static CodeGenerateResult scanTemplatesAndProcess(File templateFolder,Map<String,Object> templateModel){
		List<String> exceptionFileList = Lists.newArrayList();
		List<String> successFileList = Lists.newArrayList();
		List<String> exceptionTraceList = Lists.newArrayList();
		
		List<File> templateFiles = getAllProcessingFile(templateFolder);
		
		String outputFolder = (String)templateModel.get("outputDir");
		
		CodeGenerateResult result = new CodeGenerateResult();
		result.setOutputFolder(outputFolder);
		
		for(File file:templateFiles){
			if(isIgnoreFile(file)){
				continue;
			}
			Configuration conf = getFreeMarkerConfiguration(templateFolder);
			
			String templateFile = getRelativePath(templateFolder, file);
			String outputFilePath = processTemplateString(templateFile, templateModel, conf);
			try {
				
				System.out.println("outputFilePath = " + outputFilePath);
				String absoluteOutputFilePath = outputFolder + File.separator + outputFilePath;
				FileUtil.createOrExistsFile(absoluteOutputFilePath);
				
				
				Template template = getFreeMarkerTemplate(templateFolder, templateFile);
				template.setOutputEncoding("UTF-8");
				
				processTemplate(template, absoluteOutputFilePath, templateModel);
				
				successFileList.add(outputFilePath);
				
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
				exceptionFileList.add(outputFilePath);
				exceptionTraceList.add(ExceptionUtils.getStackTrace(e));
			}
			
			
		}
		result.setSuccessFileList(successFileList);
		result.setExceptionFileList(exceptionFileList);
		result.setExceptionTraceList(exceptionTraceList);
		
		return result;
	}
	
	/**
	 * Gets the free marker template.
	 *
	 * @param templateFolder the template folder
	 * @param tempalteName the tempalte name
	 * @return the free marker template
	 * @throws IOException Signals that an I/O exception has occurred.
	 */
	public static Template getFreeMarkerTemplate(File templateFolder,String tempalteName) throws IOException{
		Configuration conf = getFreeMarkerConfiguration(templateFolder);
		return conf.getTemplate(tempalteName);
	}
	
	/**
	 * Gets the relative path.
	 *
	 * @param templateFolder the template folder
	 * @param templateFile the template file
	 * @return the relative path
	 */
	public static String getRelativePath(File templateFolder, File templateFile){
		if(templateFolder.equals(templateFile)){
			return "";
		}
		String relativePath = templateFile.getAbsolutePath().replaceAll(Matcher.quoteReplacement(templateFolder.getAbsolutePath()), "");
		if(relativePath.startsWith("/")){
			return relativePath.substring(1);
		}
		return relativePath;
	}
	
	/**
	 * Gets the free marker configuration.
	 *
	 * @param templateFolder the template folder
	 * @return the free marker configuration
	 */
	public static Configuration getFreeMarkerConfiguration(File templateFolder){
		Configuration conf = new Configuration();
		try {
			FileTemplateLoader templateLoader = new FileTemplateLoader(templateFolder);
			
			conf.setTemplateLoader(templateLoader);
			conf.setNumberFormat("###############");
			conf.setBooleanFormat("true,false");
			conf.setDefaultEncoding("UTF-8");
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return conf;
	}
	
	/**
	 * Process template.
	 *
	 * @param template the template
	 * @param outputFile the output file
	 * @param templateModel the template model
	 * @throws IOException Signals that an I/O exception has occurred.
	 * @throws TemplateException the template exception
	 */
	public static void processTemplate(Template template, String outputFile, Map<String,Object> templateModel) throws IOException,TemplateException{
		Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outputFile),"UTF-8"));
		template.process(templateModel, out);
	}
	
	/**
	 * Process template string.
	 *
	 * @param templateString the template string
	 * @param model the model
	 * @param conf the conf
	 * @return the string
	 */
	public static String processTemplateString(String templateString, Map<String,Object> model, Configuration conf){
		StringWriter out = new StringWriter();
		try {
			Template template = new Template("templateString...", new StringReader(templateString),conf);
			template.process(model, out);
			return out.toString();
		} catch (Exception e) {
			throw new IllegalStateException("cannot process templateString:"+templateString+" cause:"+e,e);
		}
	}
	
	
	
	/**
	 * The main method.
	 *
	 * @param args the arguments
	 */
	public static void main(String[] args) {
		GenerateUtil util = new GenerateUtil();
		
		
		Object path = GenerateUtil.class.getResourceAsStream("/config.properties");
		System.out.println("path="+path);
		String templateFolder = "/Users/xiwei/git/backend/backend/src/main/resources/templates";
		Map<String,Object> model = Maps.newHashMap();
		model.put("ClassName", "UserModel");
		model.put("basePackageName", "com.xw");
		model.put("basePackageDir", "com/xw");
		
		scanTemplatesAndProcess(new File(templateFolder), model);
	}
}
